package cg.object.builder.actions;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.IVariableBinding;
import org.eclipse.jdt.core.dom.Modifier;
import org.eclipse.jdt.internal.corext.dom.ASTNodes;
import org.eclipse.jdt.internal.corext.dom.NodeFinder;
import org.eclipse.jdt.internal.corext.refactoring.util.RefactoringASTParser;
import org.eclipse.jdt.internal.ui.JavaPlugin;
import org.eclipse.jdt.internal.ui.dialogs.SourceActionDialog;
import org.eclipse.jdt.internal.ui.javaeditor.CompilationUnitEditor;
import org.eclipse.jdt.ui.IWorkingCopyManager;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.IWorkbenchWindowActionDelegate;
import org.eclipse.ui.PlatformUI;

import cg.object.builder.dialogs.GenerateFluentBuilderDialog;

/**
 * Action classe
 * 
 * @author roucouj@gmail.com
 * 
 */
@SuppressWarnings("restriction")
public class GenerateFluentBuilderAction implements IWorkbenchWindowActionDelegate {

	private ISelection selection;

	private CompilationUnitEditor fEditor;
	private CompilationUnit fUnit;
	private ITypeBinding fTypeBinding;
	private IType fType;

	private List<IVariableBinding> fFields;
	private List<IVariableBinding> fInheritedFields;

	public void run(IAction action) {
		System.out.println("run");

		try {
			if (!initializeRunFromSelection()) {
				return;
			}
		} catch (JavaModelException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		if (!generateCandidates()) {
			// TODO
			// MessageDialog.openInformation(getShell(), getErrorCaption(),
			// getNoMembersError());
			// notifyResult(false);
			return;
		}

		for (IVariableBinding field : fFields) {
			System.out.println(field);
		}
		for (IVariableBinding field : fInheritedFields) {
			System.out.println(field);
		}

		Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();
		SourceActionDialog dialog;
		try {
			dialog = createDialog(shell, fType);
			final int dialogResult = dialog.open();
		} catch (JavaModelException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

	/**
	 * Method uses to check that the action is possible depending on the current
	 * selection.
	 */
	public void selectionChanged(IAction action, ISelection selection) {

		this.selection = selection;
		boolean enable = true;

		// System.out.println("selectionChanged" + selection);
		// System.out.println(selection.getClass());

		if (selection instanceof ITextSelection) {
			IEditorPart editorPart = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage()
					.getActiveEditor();
			if (!(editorPart instanceof CompilationUnitEditor)) {
				enable = false;
			}
		} else if (selection instanceof IStructuredSelection) {
			IStructuredSelection ss = (IStructuredSelection) selection;
			if (ss.size() != 1) {
				enable = false;
			} else if (!(ss.getFirstElement() instanceof ICompilationUnit || ss.getFirstElement() instanceof IType)) {
				enable = false;
			}
		}

		action.setEnabled(enable);

	}

	public void dispose() {
		// System.out.println("dispose");
		// TODO Auto-generated method stub

	}

	public void init(IWorkbenchWindow window) {
		// System.out.println("****************  init *********************");
		// TODO Auto-generated method stub

	}

	private boolean initializeRunFromSelection() throws JavaModelException {
		if (selection instanceof IStructuredSelection) {
			fType = getSelectedType((IStructuredSelection) selection);
		} else if (selection instanceof ITextSelection) {
			fType = getSelectedType();
		}
		System.out.println(fType);
		if (fType == null) {
			return false;
		}

		initialize(fType);

		return true;

	}

	private IType getSelectedType() throws JavaModelException {
		IEditorPart editorPart = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().getActiveEditor();
		if (editorPart instanceof CompilationUnitEditor) {
			IWorkingCopyManager manager = JavaPlugin.getDefault().getWorkingCopyManager();
			ICompilationUnit unit = manager.getWorkingCopy(editorPart.getEditorInput());
			final IType type = unit.findPrimaryType();
			if (type != null && type.isClass())
				return type;
		}
		return null;
	}

	/**
	 * @see org.eclipse.jdt.ui.actions.GenerateMethodAbstractAction
	 * @param selection
	 * @return
	 * @throws JavaModelException
	 */
	private IType getSelectedType(final IStructuredSelection selection) throws JavaModelException {
		if (selection.size() == 1 && selection.getFirstElement() instanceof IType) {
			final IType type = (IType) selection.getFirstElement();
			if (type.getCompilationUnit() != null && type.isClass())
				return type;
		} else if (selection.getFirstElement() instanceof ICompilationUnit) {
			final ICompilationUnit unit = (ICompilationUnit) selection.getFirstElement();
			final IType type = unit.findPrimaryType();
			if (type != null && type.isClass())
				return type;
		}
		return null;
	}

	/**
	 * @see org.eclipse.jdt.ui.actions.GenerateMethodAbstractAction
	 * @param type
	 * @throws JavaModelException
	 */
	private void initialize(IType type) throws JavaModelException {
		RefactoringASTParser parser = new RefactoringASTParser(AST.JLS3);
		fUnit = parser.parse(type.getCompilationUnit(), true);
		fTypeBinding = null;
		// type cannot be anonymous
		final AbstractTypeDeclaration declaration = (AbstractTypeDeclaration) ASTNodes.getParent(
				NodeFinder.perform(fUnit, type.getNameRange()), AbstractTypeDeclaration.class);
		if (declaration != null)
			fTypeBinding = declaration.resolveBinding();
	}

	/**
	 * @see org.eclipse.jdt.ui.actions.GenerateToStringAction
	 * @return
	 */
	private boolean generateCandidates() {
		IVariableBinding[] candidateFields = fTypeBinding.getDeclaredFields();
		fFields = new ArrayList<IVariableBinding>();
		// fSelectedFields = new ArrayList();
		for (int i = 0; i < candidateFields.length; i++) {
			if (!Modifier.isStatic(candidateFields[i].getModifiers())) {
				fFields.add(candidateFields[i]);
				// if (!Modifier.isTransient(candidateFields[i].getModifiers()))
				// fSelectedFields.add(candidateFields[i]);
			}
		}

		fInheritedFields = new ArrayList<IVariableBinding>();
		ITypeBinding typeBinding = fTypeBinding;
		while ((typeBinding = typeBinding.getSuperclass()) != null) {
			candidateFields = typeBinding.getDeclaredFields();
			for (int i = 0; i < candidateFields.length; i++) {
				if (!Modifier.isPrivate(candidateFields[i].getModifiers())
						&& !Modifier.isStatic(candidateFields[i].getModifiers())
						&& !contains(fFields, candidateFields[i]) && !contains(fInheritedFields, candidateFields[i])) {
					fInheritedFields.add(candidateFields[i]);
				}
			}
		}

		return true;
	}

	/**
	 * @see org.eclipse.jdt.ui.actions.GenerateToStringAction
	 * @param inheritedFields
	 * @param member
	 * @return
	 */
	private boolean contains(List inheritedFields, Object member) {
		for (Iterator iterator = inheritedFields.iterator(); iterator.hasNext();) {
			Object object = iterator.next();
			if (object instanceof IVariableBinding && member instanceof IVariableBinding)
				if (((IVariableBinding) object).getName().equals(((IVariableBinding) member).getName()))
					return true;
			if (object instanceof IMethodBinding && member instanceof IMethodBinding)
				if (((IMethodBinding) object).getName().equals(((IMethodBinding) member).getName()))
					return true;
		}
		return false;
	}

	/**
	 * @see org.eclipse.jdt.ui.actions.GenerateToStringAction
	 * @param shell
	 * @param type
	 * @return
	 * @throws JavaModelException
	 */
	private SourceActionDialog createDialog(Shell shell, IType type) throws JavaModelException {
		IVariableBinding[] fieldBindings = (IVariableBinding[]) fFields.toArray(new IVariableBinding[0]);
		IVariableBinding[] inheritedFieldBindings = (IVariableBinding[]) fInheritedFields
				.toArray(new IVariableBinding[0]);
		IVariableBinding[] selectedFieldBindings = new IVariableBinding[0];
		IMethodBinding[] methodBindings = new IMethodBinding[0];
		IMethodBinding[] inheritededMethodBindings = new IMethodBinding[0];
		return new GenerateFluentBuilderDialog(shell, fEditor, type, fieldBindings, inheritedFieldBindings,
				selectedFieldBindings, methodBindings, inheritededMethodBindings);
	}

}
